// MapImage.cpp: implementation of the MapImage class.
//
//////////////////////////////////////////////////////////////////////

#include "MapImage.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


MapImage::MapImage(float ***m,int h, int w,float x_pos, float y_pos, float x_dim, float y_dim)
{
this->start_x = x_pos;
this->start_y = y_pos;
this->ww = x_dim;
this->wh = y_dim;

zoom = 1.00f;

this->h = h;
this->w = w;

this->mouseIsOver = 0;
this->isSelected = 0;

widget_x_dim = x_dim;
widget_y_dim = y_dim;

createTexture(m);

hidden = false;
}

MapImage::MapImage(char filename[],int h, int w,float x_pos, float y_pos, float x_dim, float y_dim)
{
this->start_x = x_pos;
this->start_y = y_pos;
this->ww = x_dim;
this->wh = y_dim;

zoom = 1.00f;

this->h = h;
this->w = w;

this->mouseIsOver = 0;
this->isSelected = 0;

widget_x_dim = x_dim;
widget_y_dim = y_dim;

ssgTexture* tx = new ssgTexture(filename);

texName = tx->getHandle();

hidden = false;
}

MapImage::MapImage(float ***m,int h, int w,float x_pos, float y_pos, float x_dim, float y_dim, GLuint texture_number)
{
this->start_x = x_pos;
this->start_y = y_pos;
this->ww = x_dim;
this->wh = y_dim;

zoom = 1.00f;

this->h = h;
this->w = w;

this->mouseIsOver = 0;
this->isSelected = 0;

widget_x_dim = x_dim;
widget_y_dim = y_dim;

texName = texture_number;

createTexture(m,texture_number);

hidden = false;
}

MapImage::~MapImage()
{
free(this->image);
free(this->back);
glDeleteTextures(1,&texName);
}


void MapImage::display()
{
if(!hidden)
{
  /*Set up the 2D ambient */
int fh = (float)glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ;
int fw = (float)glutGet ( (GLenum) GLUT_WINDOW_WIDTH  ) ;

  glDisable      ( GL_LIGHTING ) ;
  glDisable      ( GL_DEPTH_TEST ) ;
  glDisable      ( GL_CULL_FACE  ) ;

  glEnable       ( GL_ALPHA_TEST ) ;
  glEnable       ( GL_BLEND ) ;
  glAlphaFunc    ( GL_GREATER, 0.1f ) ;
  glBlendFunc    ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
  
  //create an projection that acts like a 2D screen
  glMatrixMode   ( GL_PROJECTION ) ;
  glPushMatrix   () ;
  glLoadIdentity () ;
  glOrtho        ( 0.0, 1.0, 0.0, fw/fh, 0.0, 1.0 ) ;

  glMatrixMode   ( GL_MODELVIEW ) ;
  glPushMatrix   () ;
  glLoadIdentity () ;     
     
   /* Draw border around images */
   float border_x = MAP_BORDER_X;
   float border_y = MAP_BORDER_Y; 
   
   
   /* Draw and paint map and widget's border when selected or mouse over*/
   if(isSelected)
   {
   glColor4f(MAP_SELECTED_COLOR);
   glBegin(GL_QUADS);
   glVertex2f(start_x - border_x, start_y - border_y);
   glVertex2f(start_x - border_x, start_y + wh + border_y);
   glVertex2f(start_x + ww + border_x, start_y + wh + border_y);
   glVertex2f(start_x + ww + border_x, start_y - border_y);
   glVertex2f(start_x - border_x, start_y - border_y);
   glEnd();
   } 
   else if(mouseIsOver)
   {
   glColor4f(MAP_MOUSE_OVER_COLOR);
   glBegin(GL_QUADS);
   glVertex2f(start_x - border_x, start_y - border_y);
   glVertex2f(start_x - border_x, start_y + wh + border_y);
   glVertex2f(start_x + ww + border_x, start_y + wh + border_y);
   glVertex2f(start_x + ww + border_x, start_y - border_y);
   glVertex2f(start_x - border_x, start_y - border_y);
   glEnd();
   } 

   /* draw default widget border */
   glColor4f(MAP_BORDER_COLOR);
   glBegin(GL_LINE_STRIP);
   glVertex2f(start_x - border_x, start_y - border_y);
   glVertex2f(start_x - border_x, start_y + wh + border_y);
   glVertex2f(start_x + ww + border_x, start_y + wh + border_y);
   glVertex2f(start_x + ww + border_x, start_y - border_y);
   glVertex2f(start_x - border_x, start_y - border_y);
   glEnd();
   
   /* Try to calculate the % part of the texture that must appear into the 
   widget... Regarding to screen dimensions, back texture dimensions (power of two)
   image dimensions (h and w). The bigger of the two is forced to 1.0f (I think :-) ) 
   and the smaller is proportioned */
   t_x_dim = screen_x_dim * widget_x_dim * texture_x_dim;
   t_y_dim = screen_y_dim * widget_y_dim * texture_y_dim;
   
   /* The zoom is the same in both directions... fit_dim is used to make vertical
   images as tall as widget and orizontal ones as fat as large as widget.*/
   float xTc = T_start_x;
   float xTd = T_start_x + t_x_dim*zoom*fit_dim;
   float yTc = T_start_y;
   float yTd = T_start_y + t_y_dim*zoom*fit_dim;
   
   // Draw the texture!!!
   glEnable(GL_TEXTURE_2D);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   glBindTexture(GL_TEXTURE_2D, texName);
   glBegin(GL_QUADS);
   glTexCoord2f(xTc, yTc); glVertex2f(start_x, start_y);
   glTexCoord2f(xTc, yTd); glVertex2f(start_x, start_y + wh);
   glTexCoord2f(xTd, yTd); glVertex2f(start_x + ww, start_y + wh);
   glTexCoord2f(xTd, yTc); glVertex2f(start_x + ww, start_y);
   glEnd();
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   glDisable(GL_TEXTURE_2D);

 /* Back to 3d view */
	
  glMatrixMode   ( GL_PROJECTION ) ;
  glPopMatrix    () ;
  glMatrixMode   ( GL_MODELVIEW ) ;
  glPopMatrix    () ;
  
  glDisable      ( GL_ALPHA_TEST ) ;
  glDisable      ( GL_BLEND ) ;
  glAlphaFunc    ( GL_ALWAYS, 0.0 ) ;
  glBlendFunc    ( GL_ONE, GL_ZERO ) ;

  glEnable     ( GL_LIGHTING ) ;
  glEnable     ( GL_DEPTH_TEST ) ;
  glEnable      ( GL_CULL_FACE  ) ;
  }// end if(!hidden)
}

int MapImage::getNextPowTwo(int n)
{
    if (n < 4)
       return 4;
    else if (n < 16)
       return 16;
    else if (n < 32)
       return 32;
    else if (n < 64)
       return 64;
    else if (n < 128)
       return 128;
    else if (n < 256)
       return 256;
    else if (n < 512)
       return 512;
    else if (n < 1024)
       return 1024;
    else if (n < 2048)
       return 2048;
    else if (n < 4096)
       return 4096; 
    else if (n < 8192)   
       return 8192;
	else return 0;
}

void MapImage::zoomIn(int z)
{
if(isSelected)
     {
     this->zoom -= MAP_ZOOM_FACTOR;
     }
}
 
void MapImage::zoomOut(int z)
{
if(isSelected)
     {
         this->zoom += MAP_ZOOM_FACTOR;
     }
}
 
void MapImage::moveUp(int u)
{
if(isSelected)
     {
     this->T_start_y += MAP_MOVE_FACTOR;
     }
}
 
void MapImage::moveDown(int d)
{
if(isSelected)
     {
     this->T_start_y -= MAP_MOVE_FACTOR;
     }
}
 
void MapImage::moveLeft(int l)
{
if(isSelected)
     {
     this->T_start_x -= MAP_MOVE_FACTOR;
     }
}
 
void MapImage::moveRight(int r)
{
if(isSelected)
     {
     this->T_start_x += MAP_MOVE_FACTOR;
     }
}
 
void MapImage::mouse(int b, int s, int x, int y)
{
if(!hidden)
{
int h = glutGet ( (GLenum) GLUT_WINDOW_HEIGHT ) ;
int w = glutGet ( (GLenum) GLUT_WINDOW_WIDTH  ) ;

float xp = (float)x / (float)w;
float yp = (float)(h-y) / (float)h;

if(w > h)
     {
     xp *= 1.0;
     yp *= 1.0; 
     }
else
    {
    xp *= h/w;
    yp *= 1.0;        
    }

if( (start_x <= xp) &&
    (start_y <= yp) &&
    (xp <= (start_x + ww)) &&
    (yp <= (start_y + wh)) ) //ww and wh should change after resize!!!
    {
    this->mouseIsOver = 1;   
    if( (b == PW_LEFT_BUTTON) && (s == PW_DOWN) )
        {
        if(isSelected){isSelected = 0;} 
        else{isSelected = 1;}
        }
    } 
else
    {
    this->mouseIsOver = 0;
    }
}// end if(!hidden)
}

void MapImage::setScreenDimensions()
{
float y = (float)glutGet ( (GLenum) GLUT_SCREEN_HEIGHT ) ;
float x = (float)glutGet ( (GLenum) GLUT_SCREEN_WIDTH  ) ;

if( x > y)             this->screen = MAP_HORIZONTAL;
else if (y > x)        this->screen = MAP_VERTICAL;
else if (y == x)       this->screen = MAP_SQUARED;

//printf("Screen is x: %f y: %f\n",x,y);

if(screen == MAP_VERTICAL)
     {
     screen_y_dim = 1.0f;
     screen_x_dim = x / y;
     //printf("Screen VERTICAL.\n");
     }
else if(screen == MAP_HORIZONTAL)
     {
     if(imagine == MAP_HORIZONTAL)
                {
                screen_x_dim = 1.0f;
                screen_y_dim = x / y;
                }
     else if(imagine == MAP_VERTICAL) 
                {
                screen_y_dim = 1.0f;
                screen_x_dim = x / y; 
                }
     else if(imagine == MAP_SQUARED)
                {
                screen_y_dim = 1.0f;
                screen_x_dim = x / y;
                }
     //printf("Screen HORIZONTAL.\n");
     }
else if(screen == MAP_SQUARED)
     {
     screen_x_dim = 1.0f;
     screen_y_dim = 1.0f;
     //printf("Screen SQUARED.\n");
     }
}

void MapImage::setTextureDimensions()
{
bh = getNextPowTwo(h);
bw = getNextPowTwo(w);

if( bw > bh)             this->texture = MAP_HORIZONTAL;
else if (bh > bw)        this->texture = MAP_VERTICAL;
else if (bw == bh)       this->texture = MAP_SQUARED;

if(texture == MAP_VERTICAL)
     {
     texture_x_dim = (float)w/(float)bw;
     texture_y_dim = (float)w/(float)bw / (bh / bw);
     //printf("Texture VERTICAL.\n");
     }
else if(texture == MAP_HORIZONTAL)
     {
     texture_x_dim = (float)h/(float)bh;
     texture_y_dim = (float)h/(float)bh;
     //printf("Texture HORIZONTAL.\n");
     }
else if(texture == MAP_SQUARED)
     {
     texture_x_dim = 1.0f;
     texture_y_dim = 1.0f;
     //printf("Texture SQUARED.\n");
     }
     

}

void MapImage::setImageDimensions()
{
//h = this->info->bmiHeader.biHeight;
//w = this->info->bmiHeader.biWidth;

if( w > h)             this->imagine = MAP_HORIZONTAL;
else if (h > w)        this->imagine = MAP_VERTICAL;
else if (w == h)       this->imagine = MAP_SQUARED;

if(imagine == MAP_VERTICAL)
     {
     image_y_dim = 1.0f;
     image_x_dim = (float)w / (float)h; 
     //printf("Image VERTICAL.\n");
     }
else if(imagine == MAP_HORIZONTAL)
     {
     image_x_dim = 1.0f;
     image_y_dim = (float)h / (float)w;
     //printf("Image HORIZONTAL.\n");
     }
else if(imagine == MAP_SQUARED)
     {
     image_x_dim = 1.0f;
     image_y_dim = 1.0f;
     //printf("Image SQUARED.\n");
     }

}

void MapImage::setWidgetDimensions()
{

if (ww > wh)              this->widget = MAP_HORIZONTAL;
else if (wh > ww)         this->widget = MAP_VERTICAL;
else if (ww == wh)        this->widget = MAP_SQUARED;

if (widget == MAP_VERTICAL)
     {
     if(imagine == MAP_HORIZONTAL)
                {
                widget_y_dim = 1.0f;
                widget_x_dim = ww / wh;
                }
     else if(imagine == MAP_VERTICAL)
                {
                widget_x_dim = 1.0f;
                widget_y_dim = wh / ww;
                }
     else if(imagine == MAP_SQUARED)
                {
                widget_y_dim = 1.0f;
                widget_x_dim = ww / wh;
                }
     //printf("Widget VERTICAL.\n");
     }
else if(widget == MAP_HORIZONTAL)
     {
     if(imagine == MAP_VERTICAL)
              {
              widget_x_dim = ww / wh;
              widget_y_dim = 1.0f;
              }
     else if(imagine == MAP_HORIZONTAL)
              {
              widget_x_dim = 1.0f;
              widget_y_dim = wh/ww;
              }
     else if(imagine == MAP_SQUARED)
              {
              widget_x_dim = ww / wh;
              widget_y_dim = 1.0f;
              }
     //printf("Widget HORIZONTAL.\n");
     }
else if(widget == MAP_SQUARED)
     {
     widget_x_dim = 1.0f;
     widget_y_dim = 1.0f;
     //printf("Widget SQUARED.\n");
     }
}

void MapImage::fitDimensions()
{
float tmp_x = screen_x_dim * widget_x_dim  * image_x_dim * texture_x_dim;
float tmp_y = screen_y_dim * widget_y_dim  * image_y_dim * texture_y_dim;

/*printf("screen_x_dim: %f widget_x_dim: %f image_x_dim: %f texture_x_dim: %f \n",screen_x_dim,widget_x_dim,image_x_dim,texture_x_dim);
printf("screen_y_dim: %f widget_y_dim: %f image_y_dim: %f texture_y_dim: %f \n",screen_y_dim,widget_y_dim,image_y_dim,texture_y_dim);
printf("FIT: tmp_x: %f tmp_y: %f\n",tmp_x, tmp_y);*/

if(imagine == MAP_VERTICAL)
         {
         fit_dim = (1.0f/tmp_y)*((float)h/(float)bh); 
         }
else if(imagine == MAP_HORIZONTAL)
    {
    fit_dim = (1.0f/tmp_x)*((float)w/(float)bw);
    }
else if (imagine == MAP_SQUARED)
     {
     fit_dim = (1.0f/tmp_y)*((float)h/(float)bh);
     }
//printf("FIT DIM: %f\n",fit_dim);
//printf("AFTER FT: %f %f\n",tmp_x*fit_dim,tmp_y*fit_dim);
}

void MapImage::createTexture(float*** m)
{
int i,i2;
int j,j2;

setImageDimensions();
setWidgetDimensions();

bh = getNextPowTwo(h+1);
bw = getNextPowTwo(w+1);

setTextureDimensions();
setScreenDimensions();

fitDimensions();

t_x_dim = screen_x_dim * widget_x_dim * texture_x_dim;
t_y_dim = screen_y_dim * widget_y_dim * texture_y_dim;

zoom_x_inc = (1.0 + (MAP_ZOOM_FACTOR)/2.0f) - 1.0f;
move_x_inc = ((MAP_MOVE_FACTOR + t_x_dim*fit_dim)/(t_x_dim*fit_dim)) - 1.0f;
total_zoom_x_inc = 0.0f;
total_move_x_inc = 0.0f;

/* allocate space for texture */
image = (GLubyte*)malloc(h*w*4*sizeof(GLubyte));
back  = (GLubyte*)malloc(bh*bw*4*sizeof(GLubyte));

for(j = 0; j < h; j++)
     {
     for(i = 0; i < 4*w; i+= 4)
			{
			image[i + j*w*4 + 0] = (GLubyte)( m[h-j-1][i/4][0] * 255.0f);
			image[i + j*w*4 + 1] = (GLubyte)( m[h-j-1][i/4][1] * 255.0f);
			image[i + j*w*4 + 2] = (GLubyte)( m[h-j-1][i/4][2] * 255.0f);
			image[i + j*w*4 + 3] = (GLubyte)( m[h-j-1][i/4][3] * 255.0f);
			}
     }

for(j2 = 0; j2 < bh; j2++)
     {
     for(i2 = 0; i2 < 4*bw; i2+= 4)
			{
			back[i2 + j2*bw*4 + 0] = 0;  
			back[i2 + j2*bw*4 + 1] = 0;
			back[i2 + j2*bw*4 + 2] = 0;
			back[i2 + j2*bw*4 + 3] = 0;
			}
     }

}

void MapImage::bindTexture()
{
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   glGenTextures(1, &texName);
   glBindTexture(GL_TEXTURE_2D, texName);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bw, bh, 0, GL_RGBA, GL_UNSIGNED_BYTE, back);

   glBindTexture(GL_TEXTURE_2D, texName);
   // It should be 0,0 not 1,1... but lines appear... so the texture will
   // be wrong of 1 pixel... but proportions are kept...
   glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1,w, h, GL_RGBA, GL_UNSIGNED_BYTE, image);

   T_start_x = 0.0f;
   T_start_y = 0.0f;
}

void MapImage::createTexture(float*** m, GLuint texture_number)
{
int i,i2;
int j,j2;

setImageDimensions();
setWidgetDimensions();

bh = getNextPowTwo(h+1);
bw = getNextPowTwo(w+1);

setTextureDimensions();
setScreenDimensions();

fitDimensions();

t_x_dim = screen_x_dim * widget_x_dim * texture_x_dim;
t_y_dim = screen_y_dim * widget_y_dim * texture_y_dim;

zoom_x_inc = (1.0 + (MAP_ZOOM_FACTOR)/2.0f) - 1.0f;
move_x_inc = ((MAP_MOVE_FACTOR + t_x_dim*fit_dim)/(t_x_dim*fit_dim)) - 1.0f;
total_zoom_x_inc = 0.0f;
total_move_x_inc = 0.0f;

/* allocate space for texture */
image = (GLubyte*)malloc(h*w*4*sizeof(GLubyte));
back  = (GLubyte*)malloc(bh*bw*4*sizeof(GLubyte));

for(j = 0; j < h; j++)
     {
     for(i = 0; i < 4*w; i+= 4)
			{
			image[i + j*w*4 + 0] = (GLubyte)( m[h-j-1][i/4][0] * 255.0f);
			image[i + j*w*4 + 1] = (GLubyte)( m[h-j-1][i/4][1] * 255.0f);
			image[i + j*w*4 + 2] = (GLubyte)( m[h-j-1][i/4][2] * 255.0f);
			image[i + j*w*4 + 3] = (GLubyte)( m[h-j-1][i/4][3] * 255.0f);
			}
     }

for(j2 = 0; j2 < bh; j2++)
     {
     for(i2 = 0; i2 < 4*bw; i2+= 4)
			{
			back[i2 + j2*bw*4 + 0] = 0;  
			back[i2 + j2*bw*4 + 1] = 0;
			back[i2 + j2*bw*4 + 2] = 0;
			back[i2 + j2*bw*4 + 3] = 0;
			}
     }

   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   glBindTexture(GL_TEXTURE_2D, texture_number);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bw, bh, 0, GL_RGBA, GL_UNSIGNED_BYTE, back);

   glBindTexture(GL_TEXTURE_2D, texture_number);
   // It should be 0,0 not 1,1... but lines appear... so the texture will
   // be wrong of 1 pixel... but proportions are kept...
   glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1,w, h, GL_RGBA, GL_UNSIGNED_BYTE, image);

   printf("Texture %d binded.\n",texture_number);

   T_start_x = 0.0f;
   T_start_y = 0.0f;
}

int MapImage::selected()
{
return isSelected;
}

void MapImage::hide()
{
this->hidden = true;
}

void MapImage::reveal()
{
this->hidden = false;
}